#ifndef _STDCPP_DEBUG_H_
#define _STDCPP_DEBUG_H_

// The majority of this code on memory debugging is stolen
// directly from the great work found here:
// http://andybrown.me.uk/wk/2011/01/01/debugging-avr-dynamic-memory-allocation/

#include "common.h"
#include <stddef.h>


typedef struct __freelist {
	size_t	sz;
	struct __freelist * nx;
} FREELIST;

extern FREELIST * __flp;
extern char * __brkval;

class mem
{
public:
	static size_t	used();
	static size_t	free();
	static size_t	maxMemBlock();
	static size_t	maxFreeBlock();
	static size_t	freeListSize();
	static size_t	maxNonFreeBlock();
	static int		numFreeBlocks();
};

size_t mem::used()
{
  size_t used;
  FREELIST *fp;

// __brkval=0 if nothing has been allocated yet

  if(__brkval==0)
    return 0;

// __brkval moves up from __malloc_heap_start to
// __malloc_heap_end as memory is used

  used=__brkval-__malloc_heap_start;

// memory free'd by you is collected in the free list and
// compacted with adjacent blocks. This, combined with malloc's
// intelligent picking of candidate blocks drastically reduces
// heap fragmentation. Anyway, since blocks in the free list
// are available to you at no cost we need to take them off.

  for(fp=__flp;fp;fp=fp->nx)
    used-=fp->sz+sizeof(size_t);

  return used;
}

size_t mem::free()
{
  return (size_t)AVR_STACK_POINTER_REG-
         (size_t)__malloc_margin-
         (size_t)__malloc_heap_start-
         used();
}

size_t mem::maxMemBlock()
{
  size_t a,b;

  a=maxFreeBlock();
  b=maxNonFreeBlock();

  return a>b ? a : b;
}

size_t mem::maxFreeBlock()
{
  FREELIST *fp;
  size_t maxsize=0;

  for(fp=__flp;fp;fp=fp->nx)
    if(fp->sz>maxsize)
      maxsize=fp->sz;

  return maxsize;
}

int mem::numFreeBlocks()
{
  FREELIST *fp;
  int i;

  for(i=0,fp=__flp;fp;fp=fp->nx,i++);
  return i;
}

size_t mem::freeListSize()
{
  FREELIST *fp;
  size_t size;

  for(size=0,fp=__flp;fp;fp=fp->nx,size+=fp->sz+sizeof(size_t));
  return size;
}

size_t mem::maxNonFreeBlock()
{
  char *cp,*brkval;

// this code is an adapted fragment from malloc() itself

  brkval=__brkval == 0 ? __malloc_heap_start : __brkval;

  if((cp=__malloc_heap_end)==NULL)
    cp=(char *)AVR_STACK_POINTER_REG-__malloc_margin;
  if(cp<=brkval)
    return 0;

  return cp-brkval;
}




#endif /* DEBUG_H_ */
